#include "eGUI.hpp"

void * eGUI_TextList::operator new(std::size_t size)
{
    return eGUI_Malloc(size);
}
void eGUI_TextList::operator delete(void* ptr)
{
    if (ptr)
        eGUI_Free(ptr);
}
eGUI_TextList::eGUI_TextList(eGUI &egui)
{
    if (this == NULL) return;
    this->root = &egui;
    this->cursor = 0;
    this->itemList = listCreate();
    if (this->itemList == NULL)
    {
        eGUI_LOG("List Create Error\n");
    }
    this->scrollBarReflashFlag = 1;
}
eGUI_TextList::~eGUI_TextList(void)
{
    if (this->itemList == NULL) return;
    if (this->itemList->len > 0)
        listDatasFree(this->itemList, eGUI_Free);
    listDestroy(&this->itemList);
}

uint16_t eGUI_TextList::getItemSelect(void)
{
    return this->topTextIndex + this->cursor;
}

eGUI_ItemString *eGUI_TextList::getItemString(void)
{
    uint16_t id = this->getItemSelect();
    return getItemString(id);
}
eGUI_ItemString *eGUI_TextList::getItemString(uint16_t Index)
{
    return (eGUI_ItemString*)listRead(this->itemList, Index);
}

eGUI_State eGUI_TextList::addItemString(const char *pstr,
                                        uint8_t type,
                                        void (*draw)(char *, size_t),
                                        void (*click)(uint32_t))
{
    eGUI_ItemString *pItem;
    if (this->itemList == NULL) return eGUI_ERROR;
    pItem = (eGUI_ItemString *)eGUI_Malloc(sizeof(eGUI_ItemString));
    if (pItem == NULL) return eGUI_ERROR;
    pItem->type = type;
    switch (type)
    {
        case ITEM_STRING_TYPE_CCHAR:
            pItem->u.pStr = pstr;
            break;
        case ITEM_STRING_TYPE_VCHAR:
                pItem->u.eStr = new eString(pstr);
            if (pItem->u.eStr == NULL)
                goto exit;
            break;
        default:
            goto exit;
    }
    pItem->draw  = draw;
    pItem->click = click;
    this->scrollBarReflashFlag = true;
    if (listPusht(this->itemList, pItem) == 0)
    {
        exit:
        eFree(pItem);
        return eGUI_ERROR;
    }
    return eGUI_OK;
}

void eGUI_TextList::deleteItemString(int index)
{
    eGUI_ItemString *pdata;
    if (this->itemList == NULL) return;
    pdata = (eGUI_ItemString*)listPop(this->itemList, index);
    if (pdata)
    {
        LOG_FAST("list delete:%d\n",index);
        switch (pdata->type)
        {
        case ITEM_STRING_TYPE_VCHAR:
            delete pdata->u.eStr;
            break;
        case ITEM_STRING_TYPE_CCHAR:
            break;
        default:
            break;
        }
        eGUI_Free(pdata);
        if (this->itemList->len <= this->root->textRows) /* 页面内容小于页面显示数量 */
        {
            this->topTextIndex = 0;
            this->root->cleanFlag = true;
            if (this->cursor >= this->itemList->len)
            {
                this->cursor = this->itemList->len-1;
            }
        }
        else if (this->itemList->len - this->topTextIndex < this->root->textRows)
        {
            this->topTextIndex -= 1; /* 删除显示界面上的最后一个item时将top上移避免空行 */
        }
        this->scrollBarReflashFlag = true;
    }
}

eGUI_TextList& eGUI_TextList::operator<<(const char *cstr)
{
    this->addItemString(cstr, ITEM_STRING_TYPE_CCHAR, NULL, NULL);
    return *this;
}

eGUI_TextList& eGUI_TextList::operator<<(char *vstr)
{
    this->addItemString(vstr, ITEM_STRING_TYPE_VCHAR, NULL, NULL);
    return *this;
}

eGUI_TextList& eGUI_TextList::operator<<(eString &estr)
{
    this->addItemString(estr.c_str(), ITEM_STRING_TYPE_VCHAR, NULL, NULL);
    return *this;
}

eGUI_TextList& eGUI_TextList::operator<<(void (*draw)(char *, size_t))
{
    this->addItemString(NULL, 0, draw, NULL);
    return *this;
}

eGUI_TextList& eGUI_TextList::operator<<(void (*click)(uint32_t))
{
    eGUI_ItemString *pItem;
    if (this->itemList)
    {
        pItem = (eGUI_ItemString*)listRead(this->itemList, this->itemList->len-1);
        if (pItem)
            pItem->click = click;
    }
    return *this;
}

void eGUI_TextList::drawScrollBar(void)
{
    const uint16_t SLIDER_BG_COLOR = 0xC618;
    const uint16_t SLIDER_FG_COLOR = 0x8410;
    const uint16_t SLIDER_WIDTH    = 10;

    scrollBarReflashFlag = false;
    int sliderStart = this->topTextIndex / (float)this->itemList->len * this->root->yPixel;
    int sliderSize = this->root->textRows / (float)this->itemList->len * this->root->yPixel;
    if (sliderSize > this->root->yPixel) sliderSize = this->root->yPixel;
    for (size_t i = 0; i < sliderStart; i++)
    {
        for (size_t j = 1; j <= SLIDER_WIDTH; j++)
        {
            eGUI_DrawPixel(this->root->xPixel-j, i, SLIDER_BG_COLOR);
        }
    }
    for (size_t i = sliderStart; i < sliderStart+sliderSize; i++)
    {
        for (size_t j = 1; j <= SLIDER_WIDTH; j++)
        {
            eGUI_DrawPixel(this->root->xPixel-j, i, SLIDER_FG_COLOR);
        }
    }
    for (size_t i = sliderStart+sliderSize; i < this->root->yPixel; i++)
    {
        for (size_t j = 1; j <= SLIDER_WIDTH; j++)
        {
            eGUI_DrawPixel(this->root->xPixel-j, i, SLIDER_BG_COLOR);
        }
    }
}

void eGUI_TextList::drawCursor(int row)
{
    BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
    BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
    eGUI_WriteLine(0, row, this->root->textBuffer[row]);
    BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
    BSP_LCD_SetBackColor(LCD_COLOR_WHITE);
}

void eGUI_TextList::show(void)
{
    size_t displayRows = 0;
    size_t cpyLen;
    const char* pstr;
    if (this->itemList == NULL) return;
    if (this->itemList->len <= this->root->textRows)
    {
        displayRows = this->itemList->len;
    }
    else if (this->itemList->len - this->topTextIndex <= this->root->textRows)
    {
        displayRows = this->itemList->len - this->topTextIndex;
    }
    else
    {
        displayRows = this->root->textRows;
    }
    BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
    BSP_LCD_SetBackColor(LCD_COLOR_WHITE);
    for (size_t i = 0; i < displayRows; i++)
    {
        // LOG_INFO("GUI","display index:%d,len:%d,sel:%d\n",i,this->itemList->len,this->getItemSelect());
        eGUI_ItemString *pItem = (eGUI_ItemString*)listRead(this->itemList, this->topTextIndex+i);
        if (pItem == NULL || i >= this->itemList->len) break;
        this->root->textBuffer[i][this->root->textColumns] = '\0';
        switch (pItem->type)
        {
        case ITEM_STRING_TYPE_CCHAR:
            cpyLen = strlen(pItem->u.pStr);
            pstr = pItem->u.pStr;
            break;
        case ITEM_STRING_TYPE_VCHAR:
            cpyLen = pItem->u.eStr->length();
            pstr = pItem->u.eStr->c_str();
            break;
        default:
            break;
        }
        if (this->root->textColumns <= cpyLen)
            cpyLen = this->root->textColumns-1;
        memcpy(this->root->textBuffer[i], pstr, cpyLen);
        if (pItem->draw != NULL)
        {
            pItem->draw((char*)this->root->textBuffer[i], this->root->textColumns);
            cpyLen = strlen((char*)this->root->textBuffer[i]);
        }
        if (cpyLen < this->root->textColumns-1)
            memset(this->root->textBuffer[i]+cpyLen, ' ', this->root->textColumns-cpyLen-1);
        
        if (this->cursorDisplayFlag && i == this->cursor) this->drawCursor(this->cursor);
        else eGUI_WriteLine(0, i, this->root->textBuffer[i]);
    }
    if (this->scrollBarReflashFlag) this->drawScrollBar();
}

eGUI_State eGUI_TextList::movePage(int num)
{
    if (this->itemList == NULL) return eGUI_ERROR;
    if (num < 0 && this->topTextIndex + num >= 0)
    {
        this->topTextIndex += num;
    }
    else if ( num > 0 &&
            this->topTextIndex + num + this->root->textRows <= this->itemList->len)
    {
        this->topTextIndex += num;
    }
    this->scrollBarReflashFlag = true;
    return eGUI_OK;
}
eGUI_State eGUI_TextList::moveCursor(int num)
{
    uint16_t itemSelect = this->topTextIndex + this->cursor;
    if (this->itemList == NULL) return eGUI_ERROR;
    if (num > 0 && itemSelect + num < this->itemList->len)
    {
        this->cursor += num;
        if (this->cursor >= this->root->textRows)
        {
            movePage(num);
            this->cursor = this->root->textRows - 1;
        }
    }
    else if (num < 0 && itemSelect + num >= 0)
    {
        this->cursor += num;
        if (this->cursor < 0)
        {
            movePage(num);
            this->cursor = 0;
        }
    }
    else
    {
        if (this->cursor > this->root->textRows)
        {
            this->cursor = 0;
        }
        return eGUI_PERROR;
    }
    this->cursorDisplayFlag = true;
    return eGUI_OK;
}
